﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Farigola.Engine.SystemData;
using Farigola.Engine.SystemData.Entity;
using Farigola.Engine.SystemData.Entity.Type;
using Farigola.Engine.SystemData.Entity.inst;

namespace Farigola.Engine.Factories
{
    public class Factory
    {
        //=====================================================================
        #region Static Data members.

        /// <summary>
        /// The factory to build all object: workspace, project, and all
        ///  domain objects of the application.
        /// its a unique instance.
        /// </summary>
        //private static Factory _factory;

        /// <summary>
        /// only available for engine objects, object that are present in the library Farigola.Engine
        /// </summary>
        /// <returns></returns>
        //internal static Factory GetFactory()
        //{
        //    if (_factory == null)
        //        _factory = new Factory();
        //    return _factory;
        //}
        #endregion

        // Implement IDisposable.
        // Do not make this method virtual.
        // A derived class should not be able to override this method.
        //public static void Dispose()
        //{
        //    if (_factory == null)
        //        return;

        //    _factory = null;
        //}

        //=====================================================================
        #region Private Data members.

        /// <summary>
        /// The unique errors manager of the engine application.
        /// Provided to main object in the engine (workspace, project,...).
        /// </summary>
        //private ErrorMgr _errorMgr;

        /// <summary>
        /// Manager of tasks activity in the application (trace)
        /// Manage alos errors occurs in the engine.
        /// </summary>
        private AppActivityMgr _appActivityMgr;

        /// <summary>
        /// the factory to build common object.
        /// </summary>
        private FactoryCommon _factoryCommon;

        /// <summary>
        /// the factory to build entities, types and instances.
        /// </summary>
        private FactoryEntity _factoryEntity;

        /// <summary>
        /// the factory to build data value.
        /// </summary>
        private FactoryDataValue _factoryDataValue;

        #endregion

        //=====================================================================
        #region Constructors.

        //---------------------------------------------------------------------
        /// <summary>
        /// Constructor.
        /// </summary>
        public Factory()
        {
            _factoryCommon = new FactoryCommon(_appActivityMgr);

            _factoryEntity = new FactoryEntity(_factoryCommon);

            _factoryDataValue = new FactoryDataValue(_factoryCommon);
        }

        #endregion

        //=====================================================================
        #region get/set methods.

        //---------------------------------------------------------------------
        public SystemDataMgr GetSystemDataMgr()
        {
            return _factoryCommon.GetSystemDataMgr();
        }

        public FactoryCommon GetFactoryCommon()
        {
            return _factoryCommon;
        }

        //---------------------------------------------------------------------
        public FactoryEntity GetFactoryEntity()
        {
            return _factoryEntity;
        }

        //---------------------------------------------------------------------
        /// <summary>
        /// Return the factory to build DataValue.
        /// </summary>
        /// <returns></returns>
        public FactoryDataValue GetFactoryDataValue()
        {
            return _factoryDataValue;
        }

        #endregion

        //=====================================================================
        #region Create workspace, project methods.

        //---------------------------------------------------------------------
        /// <summary>
        /// Create the (unique) error manager for the engine application.
        /// </summary>
        /// <returns></returns>
        //public ErrorMgr CreateErrorMgr()
        //{
        //    _errorMgr = new ErrorMgr();
        //    return _errorMgr;
        //}

        //---------------------------------------------------------------------
        /// <summary>
        /// Create the (unique) application task activity manager for the engine application.
        /// </summary>
        /// <returns></returns>
        public AppActivityMgr CreateAppActivityMgr()
        {
            _appActivityMgr = new AppActivityMgr();

            _factoryCommon.SetAppActivityMgr(_appActivityMgr);

            return _appActivityMgr;
        }

        //---------------------------------------------------------------------
        /// <summary>
        /// create the (unique) system data manager.
        /// </summary>
        /// <returns></returns>
        public SystemDataMgr CreateSystemDataMgr()
        {
            return _factoryCommon.CreateSystemDataMgr(this);
        }

        //---------------------------------------------------------------------
        public TypeMgr CreateTypeMgr()
        {
            return _factoryCommon.CreateTypeMgr();
        }

        //---------------------------------------------------------------------
        /// <summary>
        /// create the entity finder of the project.
        /// </summary>
        /// <param name="systemDataMgr"></param>
        /// <param name="typeMgr"></param>
        /// <returns></returns>
        public EntityFinder CreateEntityFinder(SystemDataMgr systemDataMgr, TypeMgr typeMgr)
        {
            return _factoryCommon.CreateEntityFinder(systemDataMgr, typeMgr); 
        }

        //---------------------------------------------------------------------
        /// <summary>
        /// create the (unique) workspace of the engine core.
        /// </summary>
        /// <returns></returns>
        public Workspace CreateWorkspace()
        {
            return _factoryCommon.CreateWorkspace(this);
        }

        //---------------------------------------------------------------------
        /// <summary>
        /// Create a new empty project.
        /// low-level method.
        /// Used when loading project from a save.
        /// </summary>
        /// <param name="id"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        public ProjectStruct CreateEmptyProjectStruct(EntityId id, String name, DateTime dateCre)
        {
            // check the name
            if (!_factoryCommon.checkSyntaxNameProject(name))
                return null;

            // create the object and provide the factory (and the sysmteDataMgr throught the factory)
            ProjectStruct prjStruct = new ProjectStruct(this, _appActivityMgr);
            //Project prj = new Project(prjStruct);

            prjStruct.SetId(id);
            prjStruct.SetName(name);
            prjStruct.SetDateCreation(dateCre);
            return prjStruct;
        }

        //---------------------------------------------------------------------
        /// <summary>
        /// Create a new empty project.
        /// low-level method.
        /// Used when loading project from a save.
        /// </summary>
        /// <param name="id"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        public Project CreateEmptyProject(EntityId id, String name, DateTime dateCre)
        {         
            ProjectStruct projectStruct = CreateEmptyProjectStruct(id, name, dateCre);
            if (projectStruct == null)
                return null;

            return new Project(projectStruct);
        }

        //---------------------------------------------------------------------
        /// <summary>
        /// Create a new empty project.
        /// low-level method.
        /// Used when loading project from a save.
        /// </summary>
        /// <param name="id"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        public Project CreateEmptyProject(EntityId id, String name)
        {
            return CreateEmptyProject(id,name, DateTime.Now);
        }

        //---------------------------------------------------------------------
        /// <summary>
        /// create a new project.
        /// High-level method, just provide the name.
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public Project CreateProject(String name)
        {
            // generate a new id
            EntityId id = _factoryCommon.CreateEntityId();

            // create an empty project
            ProjectStruct prjStruct = CreateEmptyProjectStruct(id, name, DateTime.Now);

            if (prjStruct == null)
                return null;

            // create the root folder of the project: $$$prj_root, $$$prj_root_system, $$$prj_root_user
            EntInstFolder rootFolder = _factoryEntity.CreateAllRootFolderProject();
            bool res = prjStruct.SetAllRootFolder(rootFolder);

            Project prj = new Project(prjStruct);
            return prj;
        }

        #endregion

        //=====================================================================
        #region Create types methods.

        //---------------------------------------------------------------------
        /// <summary>
        /// Build specials types like folder type.
        /// </summary>
        /// <param name="systemDataMgr"></param>
        /// <returns></returns>
        private bool CreateBuiltInTypeSpecial(SystemDataMgr systemDataMgr)
        {
            // create a new id based on a defined id for the folder type.
            EntityId id = _factoryCommon.CreateEntityId(EntityDef.TypeFolderValGuid);

            // create the folder type
            EntTypeFolder typeFolder = new EntTypeFolder(id, EntityDef.TypeFolderName);

            // save it in the manager
            return systemDataMgr.AddEntTypeFolder(typeFolder);
        }

        //---------------------------------------------------------------------
        /// <summary>
        /// create all built-in entities basic type (int, string,...).
        /// </summary>
        /// <param name="systemDataMgr"></param>
        /// <returns></returns>
        private bool CreateAllBuiltInEntTypeBasic(SystemDataMgr systemDataMgr)
        {
            bool res;

            // create a new id based on a defined id for the folder type.
            EntityId id = _factoryCommon.CreateEntityId(EntityDef.TypeStringValGuid);

            // create the string type
            EntTypeString typeString = new EntTypeString(id, EntityDef.TypeStringName);

            // save it in the manager
            res = systemDataMgr.AddEntTypeString(typeString);
            if (!res)
                return false;

            // add the int, double..
            // todo:

            return true;
        }

        //---------------------------------------------------------------------
        /// <summary>
        /// create type structure: list, array,... 
        /// </summary>
        /// <param name="systemDataMgr"></param>
        /// <returns></returns>
        private bool CreateAllBuiltInEntTypeStruct(SystemDataMgr systemDataMgr)
        {
            bool res;

            // create a new id based on a defined id for the folder type.
            EntityId id = _factoryCommon.CreateEntityId(EntityDef.TypeStructListValGuid);

            // create the struct list type
            EntTypeStructList typeStructList = new EntTypeStructList(id, EntityDef.TypeStructListName);

            // save it in the manager
            res = systemDataMgr.AddEntTypeStructList(typeStructList);
            if (!res)
                return false;

            // create the type struct array,...
            // todo:

            return true;
        }

        //---------------------------------------------------------------------
        /// <summary>
        /// Create all built-in data of the application: types and instances entities.
        /// called by the workspace.
        ///  
        /// type Class are not saved in typeMgr, its a meta-type, its not a final type.
        /// Its possible to create type class instance, exp: type Class Person,...
        /// </summary>
        /// <param name="systemDataMgr"></param>
        /// <returns></returns>
        public bool CreateAllBuiltInSystemData(SystemDataMgr systemDataMgr)
        {
            bool resEntSpecial, resEntTypeBasic, resEntTypeStruct;

            if (systemDataMgr == null)
                return false;

            // check if not yet created
            // todo:

            // todo: faire un clean, sur?
            systemDataMgr.clearAll();

            // first, create all built-in special entities type like type folder
            resEntSpecial = CreateBuiltInTypeSpecial(systemDataMgr);

            // create all built-in entities basic type (int, sting,...) 	
            resEntTypeBasic = CreateAllBuiltInEntTypeBasic(systemDataMgr);

            //  create type structure: list, array,... 
            resEntTypeStruct = CreateAllBuiltInEntTypeStruct(systemDataMgr);

            // create type processing
            // todo:

            // create others built-in type: data or processing
            return resEntSpecial || resEntTypeBasic || resEntTypeStruct;
        }

        #endregion

        //=====================================================================
        // xxx methods.

    }
}
